﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.IO.Compression;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using DaiChong.Lib.Type;
using DaiChong.Lib.Util;

namespace DaiChong.Lib.Http
{

    /// <summary>
    /// 发送HTTP请求的类
    /// </summary>
    public class Submit
    {
        #region - 变量 -

        WebProxy proxy = null;

        private string proxyString = string.Empty;

        private string forWord = string.Empty;

        private int timeOut = 50;

        protected Uri uri;

        protected string url;

        #endregion

        #region - 属性 -

        /// <summary>
        /// 请求返回的HTML
        /// </summary>
        public string Html { get; set; }

        /// <summary>
        /// 是否是WAP请求
        /// </summary>
        public bool IsWap { get; set; }

        /// <summary>
        /// 是否是火狐浏览器请求
        /// </summary>
        public bool IsFirefox { get; set; }

        public PostFormat PostFormat { get; set; }

        /// <summary>
        /// 代理地址
        /// </summary>
        public string ProxyAddress
        {
            set
            {
                if (!string.IsNullOrEmpty(value))
                {
                    proxy = new WebProxy(value);
                    if (IsForWord)
                    {
                        forWord = value;
                    }
                }
                else
                {
                    proxy = null;
                }
                proxyString = value;
            }
            get
            {
                return proxyString;
            }
        }

        /// <summary>
        /// 是否需要代理
        /// </summary>
        public bool ReqProxy { get; set; }

        /// <summary>
        /// 是否是伪装代理
        /// </summary>
        public bool IsForWord { get; set; }

        public Dictionary<string, string> Headers { get; set; }

        public int TimeOut
        {
            set
            {
                timeOut = value;
            }
            get
            {
                return timeOut;
            }
        }

        /// <summary>
        /// 设置要请求的URL
        /// </summary>
        public string URL
        {
            set { url = value; uri = new Uri(value); }
            get { return url; }
        }

        public string Referer
        {
            set;
            get;
        }

        private Encoding encode = Encoding.Default;
        public Encoding Encode
        {
            set { encode = value; }
            get { return encode; }
        }

        public CookieContainer Cookies
        {
            get;
            set;
        }

        public HttpWebRequest Request
        {
            get;
            set;
        }

        public IDictionary<string, string> GetParameters
        {
            get;
            set;
        }

        public IDictionary<string, string> PostParameters
        {
            get;
            set;
        }

        #endregion

        #region - 方法 -

        void SetProxy()
        {
            if (IsForWord)
            {
                if (!string.IsNullOrEmpty(forWord))
                {
                    Request.Headers.Add("X_FORWARDED_FOR", forWord);
                }
            }
            else
            {
                //代理
                if (proxy != null && ReqProxy)
                {
                    Request.Proxy = proxy;
                    Request.ServicePoint.Expect100Continue = false;
                }
            }
        }

        /// <summary>
        /// 发送请求
        /// </summary>
        /// <param name="method">请求方式(GET/POST)</param>
        protected virtual HttpWebResponse Send(HttpMethodType method)
        {

            if (GetParameters != null && GetParameters.Count > 0)
            {
                if (url.Contains("?"))
                {
                    url = url + "&" + WebUtils.BuildPostData(GetParameters, Encode);
                }
                else
                {
                    url = url + "?" + WebUtils.BuildPostData(GetParameters, Encode);
                }
            }
            if (url.EndsWith("?"))
            {
                url = url.TrimEnd('?');
            }
            URL = url;

            Request = (HttpWebRequest)WebRequest.Create(uri);
            Request.Timeout = TimeOut * 1000;

            if (uri.ToString().ToLower().StartsWith("https"))
            {
                ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(CheckValidationResult);
                Request.ProtocolVersion = HttpVersion.Version10;
            }

            SetProxy();

            if (!string.IsNullOrEmpty(Referer))
            {
                Request.Referer = Referer;
            }
            Request.CookieContainer = Cookies;
            SetRequestUserAgent();
            Request.KeepAlive = true;
            Request.AllowAutoRedirect = true;

            if (method == HttpMethodType.POST)
            {
                byte[] postData = Encode.GetBytes(PostFormat == PostFormat.Common ? WebUtils.BuildPostData(PostParameters, Encode) : PostParameters["Content"]);
                Request.Method = "POST";
                SetPostHead();
                Request.ContentLength = postData.Length;
                Stream newStream = Request.GetRequestStream();
                newStream.Write(postData, 0, postData.Length);
                newStream.Close();
                newStream.Dispose();
            }
            return (HttpWebResponse)Request.GetResponse();
        }

        protected void SetPostHead()
        {
            Request.ContentType = "application/x-www-form-urlencoded";
            Request.Headers.Add("Accept-Language", "zh-cn");
            Request.Headers.Add("Accept-Encoding", "gzip,deflate");
            if (Headers != null)
            {
                if (Headers.Count > 0)
                {
                    foreach (string key in Headers.Keys)
                    {
                        Request.Headers.Add(key, Headers[key]);
                    }
                }
            }
        }

        protected void SetRequestUserAgent()
        {
            if (IsWap)
            {
                Request.UserAgent = "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.805.0 Safari/535.1";
            }
            else
            {
                if (IsFirefox)
                {
                    Request.UserAgent = "Mozilla/5.0 (Windows NT 5.1; rv:5.0.1) Gecko/20100101 Firefox/5.0.1";
                }
                else
                {
                    Request.UserAgent = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022)";
                }
            }
        }

        //https
        protected bool CheckValidationResult(object sender,
            System.Security.Cryptography.X509Certificates.X509Certificate certificate,
            System.Security.Cryptography.X509Certificates.X509Chain chain,
            System.Net.Security.SslPolicyErrors errors)
        { // Always accept
            return true;
        }

        public void Get()
        {
            Html = string.Empty;
            HttpWebResponse response = Send(HttpMethodType.GET);
            GetHtml(response);
        }

        public void Post()
        {
            Html = string.Empty;
            HttpWebResponse response = Send(HttpMethodType.POST);
            GetHtml(response);
        }

        private void GetHtml(HttpWebResponse response)
        {
            try
            {
                Html = WebUtils.GetResponseAsString(response, Encode);
            }
            catch (System.Exception ex)
            {
                throw ex;
            }
        }

        public virtual Image GetImage()
        {
            Html = string.Empty;
            HttpWebResponse response = Send(HttpMethodType.GET);
            Image original = null;
            Stream stream = null;
            try
            {
                stream = response.GetResponseStream();
                original = Image.FromStream(stream);
            }
            catch (System.Exception ex)
            {
                throw ex;
            }
            finally
            {
                if (stream != null)
                {
                    stream.Close();
                }
                if (response != null)
                {
                    response.Close();
                }
            }
            return original;
        }

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();

            if (!string.IsNullOrEmpty(url))
            {
                sb.AppendLine(url);
                sb.AppendLine();
            }

            if (GetParameters != null)
            {
                sb.AppendLine(WebUtils.BuildPostData(GetParameters, Encode));
                sb.AppendLine();
            }

            if (PostParameters != null)
            {
                sb.AppendLine();
                sb.AppendLine(WebUtils.BuildPostData(PostParameters, Encode));
                sb.AppendLine();
            }

            if (!string.IsNullOrEmpty(Html))
            {
                sb.AppendLine(Html);
            }

            return sb.ToString();
        }

        public void Init()
        {
            url = string.Empty;
            uri = null;
            Referer = string.Empty;
            PostParameters = null;
            GetParameters = null;
        }

        #endregion

    }
}

namespace DaiChong.Lib.Type
{
    public enum HttpMethodType : int
    {
        GET = 0,
        POST = 1
    }

    public enum PostFormat
    {
        /// <summary>
        /// 普通
        /// </summary>
        Common = 0,

        /// <summary>
        /// 字符
        /// </summary>
        String = 1
    }
}

namespace DaiChong.Lib.Util
{
    public class ImageUtils
    {
        public static byte[] ImageToBytes(Image img)
        {
            MemoryStream ms = new MemoryStream();
            img.Save(ms, ImageFormat.Jpeg);
            byte[] by = new byte[ms.Length];
            ms.Position = 0;
            ms.Read(by, 0, Convert.ToInt32(ms.Length));
            ms.Close();
            return by;
        }
    }

    /// <summary>
    /// 网络工具类。
    /// </summary>
    public abstract class WebUtils
    {

        /// <summary>
        /// 获取表单中所有的input
        /// </summary>
        /// <param name="html"></param>
        public static Dictionary<string, string> GetInitPostData(string html)
        {
            Dictionary<string, string> dict = new Dictionary<string, string>();

            Regex regKey = new Regex(@"\sname=\s*(""(?<key>[^""]+)""|'(?<key>[^']+)'|(?<key>[\S]+))", RegexOptions.IgnoreCase);
            Regex regValue = new Regex(@"\svalue=\s*(""(?<value>[^""]*)""|'(?<value>[^']*)'|(?<value>[\S]*))", RegexOptions.IgnoreCase);
            MatchCollection matches = Regex.Matches(html, @"<(input|select|postfield)[^>]+>", RegexOptions.IgnoreCase);
            foreach (Match ma in matches)
            {
                string input = ma.Groups[0].Value;
                string key = regKey.Match(input).Groups["key"].Value;
                if (!string.IsNullOrEmpty(key))
                {
                    dict[key] = regValue.Match(input).Groups["value"].Value;
                }
            }

            return dict;
        }

        public static string GetFormAction(string html)
        {
            return Regex.Match(html, @"action=[""']?([^""'\s]+)[""']?", RegexOptions.IgnoreCase).Groups[1].Value;
        }

        /// <summary>
        /// 把响应流转换为文本。
        /// </summary>
        /// <param name="rsp">响应流对象</param>
        /// <param name="encoding">编码方式</param>
        /// <returns>响应文本</returns>
        public static string GetResponseAsString(HttpWebResponse rsp, Encoding encoding)
        {
            StringBuilder result = new StringBuilder();
            Stream stream = null;
            StreamReader reader = null;

            try
            {
                // 以字符流的方式读取HTTP响应
                stream = rsp.GetResponseStream();
                //解压缩
                if (rsp.ContentEncoding.ToLower().Contains("gzip"))
                {
                    stream = new GZipStream(stream, CompressionMode.Decompress);
                }
                reader = new StreamReader(stream, encoding);
                if (rsp.ContentLength > 0)
                {
                    // 每次读取不大于256个字符，并写入字符串
                    char[] buffer = new char[256];
                    int readBytes = 0;
                    while ((readBytes = reader.Read(buffer, 0, buffer.Length)) > 0)
                    {
                        result.Append(buffer, 0, readBytes);
                    }
                }
                else //Chunked 编码
                {
                    try
                    {
                        while (!reader.EndOfStream)
                            result.Append((char)reader.Read());
                    }
                    catch (System.IO.IOException)
                    {
                    }
                }
            }
            catch (System.Exception ex)
            {
                throw ex;
            }
            finally
            {
                // 释放资源
                if (reader != null)
                {
                    reader.Close();
                    reader.Dispose();
                }
                if (stream != null) { stream.Close(); }
                if (rsp != null) rsp.Close();
            }

            return result.ToString();
        }

        /// <summary>
        /// 组装普通文本请求参数。
        /// </summary>
        /// <param name="parameters">Key-Value形式请求参数字典</param>
        /// <returns>URL编码后的请求数据</returns>
        public static string BuildPostData(IDictionary<string, string> parameters, Encoding encoding)
        {
            StringBuilder postData = new StringBuilder();
            bool hasParam = false;

            IEnumerator<KeyValuePair<string, string>> dem = parameters.GetEnumerator();
            while (dem.MoveNext())
            {
                string name = dem.Current.Key;
                string value = dem.Current.Value;
                // 忽略参数名或参数值为空的参数
                if (!string.IsNullOrEmpty(name))
                {
                    if (hasParam)
                    {
                        postData.Append("&");
                    }
                    postData.Append(name);
                    postData.Append("=");
                    if (value == null)
                    {
                        value = string.Empty;
                    }
                    postData.Append(HttpUtility.UrlEncode(value, encoding));
                    hasParam = true;
                }
            }

            return postData.ToString();
        }
    }
}